// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/win/audio_low_latency_output_win.h"

#include <windows.h>
#include <mmsystem.h>
#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "base/win/scoped_com_initializer.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_unittest_util.h"
#include "media/audio/mock_audio_source_callback.h"
#include "media/audio/win/core_audio_util_win.h"
#include "media/base/decoder_buffer.h"
#include "media/base/seekable_buffer.h"
#include "media/base/test_data_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::Between;
using ::testing::CreateFunctor;
using ::testing::DoAll;
using ::testing::Gt;
using ::testing::InvokeWithoutArgs;
using ::testing::NotNull;
using ::testing::Return;

namespace media {

static const char kSpeechFile_16b_s_48k[] = "speech_16b_stereo_48kHz.raw";
static const char kSpeechFile_16b_s_44k[] = "speech_16b_stereo_44kHz.raw";
static const size_t kFileDurationMs = 20000;
static const size_t kNumFileSegments = 2;
static const int kBitsPerSample = 16;
static const size_t kMaxDeltaSamples = 1000;
static const char kDeltaTimeMsFileName[] = "delta_times_ms.txt";

MATCHER_P(HasValidDelay, value, "")
{
    // It is difficult to come up with a perfect test condition for the delay
    // estimation. For now, verify that the produced output delay is always
    // larger than the selected buffer size.
    return arg >= value;
}

// Used to terminate a loop from a different thread than the loop belongs to.
// |task_runner| should be a SingleThreadTaskRunner.
ACTION_P(QuitLoop, task_runner)
{
    task_runner->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
}

// This audio source implementation should be used for manual tests only since
// it takes about 20 seconds to play out a file.
class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback {
public:
    explicit ReadFromFileAudioSource(const std::string& name)
        : pos_(0)
        , previous_call_time_(base::TimeTicks::Now())
        , text_file_(NULL)
        , elements_to_write_(0)
    {
        // Reads a test file from media/test/data directory.
        file_ = ReadTestDataFile(name);

        // Creates an array that will store delta times between callbacks.
        // The content of this array will be written to a text file at
        // destruction and can then be used for off-line analysis of the exact
        // timing of callbacks. The text file will be stored in media/test/data.
        delta_times_.reset(new int[kMaxDeltaSamples]);
    }

    ~ReadFromFileAudioSource() override
    {
        // Get complete file path to output file in directory containing
        // media_unittests.exe.
        base::FilePath file_name;
        EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
        file_name = file_name.AppendASCII(kDeltaTimeMsFileName);

        EXPECT_TRUE(!text_file_);
        text_file_ = base::OpenFile(file_name, "wt");
        DLOG_IF(ERROR, !text_file_) << "Failed to open log file.";

        // Write the array which contains delta times to a text file.
        size_t elements_written = 0;
        while (elements_written < elements_to_write_) {
            fprintf(text_file_, "%d\n", delta_times_[elements_written]);
            ++elements_written;
        }

        base::CloseFile(text_file_);
    }

    // AudioOutputStream::AudioSourceCallback implementation.
    int OnMoreData(base::TimeDelta /* delay */,
        base::TimeTicks /* delay_timestamp */,
        int /* prior_frames_skipped */,
        AudioBus* dest) override
    {
        // Store time difference between two successive callbacks in an array.
        // These values will be written to a file in the destructor.
        const base::TimeTicks now_time = base::TimeTicks::Now();
        const int diff = (now_time - previous_call_time_).InMilliseconds();
        previous_call_time_ = now_time;
        if (elements_to_write_ < kMaxDeltaSamples) {
            delta_times_[elements_to_write_] = diff;
            ++elements_to_write_;
        }

        int max_size = dest->frames() * dest->channels() * kBitsPerSample / 8;

        // Use samples read from a data file and fill up the audio buffer
        // provided to us in the callback.
        if (pos_ + static_cast<int>(max_size) > file_size())
            max_size = file_size() - pos_;
        int frames = max_size / (dest->channels() * kBitsPerSample / 8);
        if (max_size) {
            dest->FromInterleaved(file_->data() + pos_, frames, kBitsPerSample / 8);
            pos_ += max_size;
        }
        return frames;
    }

    void OnError(AudioOutputStream* stream) override { }

    int file_size() { return file_->data_size(); }

private:
    scoped_refptr<DecoderBuffer> file_;
    std::unique_ptr<int[]> delta_times_;
    int pos_;
    base::TimeTicks previous_call_time_;
    FILE* text_file_;
    size_t elements_to_write_;
};

static bool ExclusiveModeIsEnabled()
{
    return (WASAPIAudioOutputStream::GetShareMode() == AUDCLNT_SHAREMODE_EXCLUSIVE);
}

static bool HasCoreAudioAndOutputDevices(AudioManager* audio_man)
{
    // The low-latency (WASAPI-based) version requires Windows Vista or higher.
    // TODO(henrika): note that we use Wave today to query the number of
    // existing output devices.
    return CoreAudioUtil::IsSupported() && audio_man->HasAudioOutputDevices();
}

// Convenience method which creates a default AudioOutputStream object but
// also allows the user to modify the default settings.
class AudioOutputStreamWrapper {
public:
    explicit AudioOutputStreamWrapper(AudioManager* audio_manager)
        : audio_man_(audio_manager)
        , format_(AudioParameters::AUDIO_PCM_LOW_LATENCY)
        , bits_per_sample_(kBitsPerSample)
    {
        AudioParameters preferred_params;
        EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
            AudioDeviceDescription::kDefaultDeviceId, true, &preferred_params)));
        channel_layout_ = preferred_params.channel_layout();
        sample_rate_ = preferred_params.sample_rate();
        samples_per_packet_ = preferred_params.frames_per_buffer();
    }

    ~AudioOutputStreamWrapper() { }

    // Creates AudioOutputStream object using default parameters.
    AudioOutputStream* Create()
    {
        return CreateOutputStream();
    }

    // Creates AudioOutputStream object using non-default parameters where the
    // frame size is modified.
    AudioOutputStream* Create(int samples_per_packet)
    {
        samples_per_packet_ = samples_per_packet;
        return CreateOutputStream();
    }

    // Creates AudioOutputStream object using non-default parameters where the
    // sample rate and frame size are modified.
    AudioOutputStream* Create(int sample_rate, int samples_per_packet)
    {
        sample_rate_ = sample_rate;
        samples_per_packet_ = samples_per_packet;
        return CreateOutputStream();
    }

    AudioParameters::Format format() const { return format_; }
    int channels() const { return ChannelLayoutToChannelCount(channel_layout_); }
    int bits_per_sample() const { return bits_per_sample_; }
    int sample_rate() const { return sample_rate_; }
    int samples_per_packet() const { return samples_per_packet_; }

private:
    AudioOutputStream* CreateOutputStream()
    {
        AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(
            AudioParameters(format_, channel_layout_, sample_rate_,
                bits_per_sample_, samples_per_packet_),
            std::string(), AudioManager::LogCallback());
        EXPECT_TRUE(aos);
        return aos;
    }

    AudioManager* audio_man_;
    AudioParameters::Format format_;
    ChannelLayout channel_layout_;
    int bits_per_sample_;
    int sample_rate_;
    int samples_per_packet_;
};

// Convenience method which creates a default AudioOutputStream object.
static AudioOutputStream* CreateDefaultAudioOutputStream(
    AudioManager* audio_manager)
{
    AudioOutputStreamWrapper aosw(audio_manager);
    AudioOutputStream* aos = aosw.Create();
    return aos;
}

class WASAPIAudioOutputStreamTest : public ::testing::Test {
public:
    WASAPIAudioOutputStreamTest()
    {
        audio_manager_ = AudioManager::CreateForTesting(message_loop_.task_runner());
        base::RunLoop().RunUntilIdle();
    }
    ~WASAPIAudioOutputStreamTest() override
    {
        audio_manager_.reset();
        base::RunLoop().RunUntilIdle();
    }

protected:
    base::MessageLoopForUI message_loop_;
    ScopedAudioManagerPtr audio_manager_;
};

// Verify that we can retrieve the current hardware/mixing sample rate
// for the default audio device.
// TODO(henrika): modify this test when we support full device enumeration.
TEST_F(WASAPIAudioOutputStreamTest, HardwareSampleRate)
{
    // Skip this test in exclusive mode since the resulting rate is only utilized
    // for shared mode streams.
    if (ExclusiveModeIsEnabled())
        return;
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));

    // Default device intended for games, system notification sounds,
    // and voice commands.
    int fs = static_cast<int>(
        WASAPIAudioOutputStream::HardwareSampleRate(std::string()));
    EXPECT_GE(fs, 0);
}

// Test Create(), Close() calling sequence.
TEST_F(WASAPIAudioOutputStreamTest, CreateAndClose)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));
    AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get());
    aos->Close();
}

// Test Open(), Close() calling sequence.
TEST_F(WASAPIAudioOutputStreamTest, OpenAndClose)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));
    AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get());
    EXPECT_TRUE(aos->Open());
    aos->Close();
}

// Test Open(), Start(), Close() calling sequence.
TEST_F(WASAPIAudioOutputStreamTest, OpenStartAndClose)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));
    AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get());
    EXPECT_TRUE(aos->Open());
    MockAudioSourceCallback source;
    EXPECT_CALL(source, OnError(aos))
        .Times(0);
    aos->Start(&source);
    aos->Close();
}

// Test Open(), Start(), Stop(), Close() calling sequence.
TEST_F(WASAPIAudioOutputStreamTest, OpenStartStopAndClose)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));
    AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get());
    EXPECT_TRUE(aos->Open());
    MockAudioSourceCallback source;
    EXPECT_CALL(source, OnError(aos))
        .Times(0);
    aos->Start(&source);
    aos->Stop();
    aos->Close();
}

// Test SetVolume(), GetVolume()
TEST_F(WASAPIAudioOutputStreamTest, Volume)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));
    AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get());

    // Initial volume should be full volume (1.0).
    double volume = 0.0;
    aos->GetVolume(&volume);
    EXPECT_EQ(1.0, volume);

    // Verify some valid volume settings.
    aos->SetVolume(0.0);
    aos->GetVolume(&volume);
    EXPECT_EQ(0.0, volume);

    aos->SetVolume(0.5);
    aos->GetVolume(&volume);
    EXPECT_EQ(0.5, volume);

    aos->SetVolume(1.0);
    aos->GetVolume(&volume);
    EXPECT_EQ(1.0, volume);

    // Ensure that invalid volume setting have no effect.
    aos->SetVolume(1.5);
    aos->GetVolume(&volume);
    EXPECT_EQ(1.0, volume);

    aos->SetVolume(-0.5);
    aos->GetVolume(&volume);
    EXPECT_EQ(1.0, volume);

    aos->Close();
}

// Test some additional calling sequences.
TEST_F(WASAPIAudioOutputStreamTest, MiscCallingSequences)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));

    AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager_.get());
    WASAPIAudioOutputStream* waos = static_cast<WASAPIAudioOutputStream*>(aos);

    // Open(), Open() is a valid calling sequence (second call does nothing).
    EXPECT_TRUE(aos->Open());
    EXPECT_TRUE(aos->Open());

    MockAudioSourceCallback source;

    // Start(), Start() is a valid calling sequence (second call does nothing).
    aos->Start(&source);
    EXPECT_TRUE(waos->started());
    aos->Start(&source);
    EXPECT_TRUE(waos->started());

    // Stop(), Stop() is a valid calling sequence (second call does nothing).
    aos->Stop();
    EXPECT_FALSE(waos->started());
    aos->Stop();
    EXPECT_FALSE(waos->started());

    // Start(), Stop(), Start(), Stop().
    aos->Start(&source);
    EXPECT_TRUE(waos->started());
    aos->Stop();
    EXPECT_FALSE(waos->started());
    aos->Start(&source);
    EXPECT_TRUE(waos->started());
    aos->Stop();
    EXPECT_FALSE(waos->started());

    aos->Close();
}

// Use preferred packet size and verify that rendering starts.
TEST_F(WASAPIAudioOutputStreamTest, ValidPacketSize)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));

    MockAudioSourceCallback source;
    // Create default WASAPI output stream which plays out in stereo using
    // the shared mixing rate. The default buffer size is 10ms.
    AudioOutputStreamWrapper aosw(audio_manager_.get());
    AudioOutputStream* aos = aosw.Create();
    EXPECT_TRUE(aos->Open());

    // Derive the expected duration of each packet.
    base::TimeDelta packet_duration = base::TimeDelta::FromSecondsD(
        static_cast<double>(aosw.samples_per_packet()) / aosw.sample_rate());

    // Wait for the first callback and verify its parameters.  Ignore any
    // subsequent callbacks that might arrive.
    EXPECT_CALL(source,
        OnMoreData(HasValidDelay(packet_duration), _, 0, NotNull()))
        .WillOnce(DoAll(QuitLoop(message_loop_.task_runner()),
            Return(aosw.samples_per_packet())))
        .WillRepeatedly(Return(0));

    aos->Start(&source);
    message_loop_.task_runner()->PostDelayedTask(
        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
        TestTimeouts::action_timeout());
    base::RunLoop().Run();
    aos->Stop();
    aos->Close();
}

// This test is intended for manual tests and should only be enabled
// when it is required to play out data from a local PCM file.
// By default, GTest will print out YOU HAVE 1 DISABLED TEST.
// To include disabled tests in test execution, just invoke the test program
// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
// environment variable to a value greater than 0.
// The test files are approximately 20 seconds long.
TEST_F(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()));

    AudioOutputStreamWrapper aosw(audio_manager_.get());
    AudioOutputStream* aos = aosw.Create();
    EXPECT_TRUE(aos->Open());

    std::string file_name;
    if (aosw.sample_rate() == 48000) {
        file_name = kSpeechFile_16b_s_48k;
    } else if (aosw.sample_rate() == 44100) {
        file_name = kSpeechFile_16b_s_44k;
    } else if (aosw.sample_rate() == 96000) {
        // Use 48kHz file at 96kHz as well. Will sound like Donald Duck.
        file_name = kSpeechFile_16b_s_48k;
    } else {
        FAIL() << "This test supports 44.1, 48kHz and 96kHz only.";
        return;
    }
    ReadFromFileAudioSource file_source(file_name);

    DVLOG(0) << "File name      : " << file_name.c_str();
    DVLOG(0) << "Sample rate    : " << aosw.sample_rate();
    DVLOG(0) << "Bits per sample: " << aosw.bits_per_sample();
    DVLOG(0) << "#channels      : " << aosw.channels();
    DVLOG(0) << "File size      : " << file_source.file_size();
    DVLOG(0) << "#file segments : " << kNumFileSegments;
    DVLOG(0) << ">> Listen to the stereo file while playing...";

    for (size_t i = 0; i < kNumFileSegments; i++) {
        // Each segment will start with a short (~20ms) block of zeros, hence
        // some short glitches might be heard in this test if kNumFileSegments
        // is larger than one. The exact length of the silence period depends on
        // the selected sample rate.
        aos->Start(&file_source);
        base::PlatformThread::Sleep(
            base::TimeDelta::FromMilliseconds(kFileDurationMs / kNumFileSegments));
        aos->Stop();
    }

    DVLOG(0) << ">> Stereo file playout has stopped.";
    aos->Close();
}

// Verify that we can open the output stream in exclusive mode using a
// certain set of audio parameters and a sample rate of 48kHz.
// The expected outcomes of each setting in this test has been derived
// manually using log outputs (--v=1).
// It's disabled by default because a flag is required to enable exclusive mode.
TEST_F(WASAPIAudioOutputStreamTest, DISABLED_ExclusiveModeBufferSizesAt48kHz)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled());

    AudioOutputStreamWrapper aosw(audio_manager_.get());

    // 10ms @ 48kHz shall work.
    // Note that, this is the same size as we can use for shared-mode streaming
    // but here the endpoint buffer delay is only 10ms instead of 20ms.
    AudioOutputStream* aos = aosw.Create(48000, 480);
    EXPECT_TRUE(aos->Open());
    aos->Close();

    // 5ms @ 48kHz does not work due to misalignment.
    // This test will propose an aligned buffer size of 5.3333ms.
    // Note that we must call Close() even is Open() fails since Close() also
    // deletes the object and we want to create a new object in the next test.
    aos = aosw.Create(48000, 240);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 5.3333ms @ 48kHz should work (see test above).
    aos = aosw.Create(48000, 256);
    EXPECT_TRUE(aos->Open());
    aos->Close();

    // 2.6667ms is smaller than the minimum supported size (=3ms).
    aos = aosw.Create(48000, 128);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 3ms does not correspond to an aligned buffer size.
    // This test will propose an aligned buffer size of 3.3333ms.
    aos = aosw.Create(48000, 144);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 3.3333ms @ 48kHz <=> smallest possible buffer size we can use.
    aos = aosw.Create(48000, 160);
    EXPECT_TRUE(aos->Open());
    aos->Close();
}

// Verify that we can open the output stream in exclusive mode using a
// certain set of audio parameters and a sample rate of 44.1kHz.
// The expected outcomes of each setting in this test has been derived
// manually using log outputs (--v=1).
// It's disabled by default because a flag is required to enable exclusive mode.
TEST_F(WASAPIAudioOutputStreamTest, DISABLED_ExclusiveModeBufferSizesAt44kHz)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled());

    AudioOutputStreamWrapper aosw(audio_manager_.get());

    // 10ms @ 44.1kHz does not work due to misalignment.
    // This test will propose an aligned buffer size of 10.1587ms.
    AudioOutputStream* aos = aosw.Create(44100, 441);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 10.1587ms @ 44.1kHz shall work (see test above).
    aos = aosw.Create(44100, 448);
    EXPECT_TRUE(aos->Open());
    aos->Close();

    // 5.8050ms @ 44.1 should work.
    aos = aosw.Create(44100, 256);
    EXPECT_TRUE(aos->Open());
    aos->Close();

    // 4.9887ms @ 44.1kHz does not work to misalignment.
    // This test will propose an aligned buffer size of 5.0794ms.
    // Note that we must call Close() even is Open() fails since Close() also
    // deletes the object and we want to create a new object in the next test.
    aos = aosw.Create(44100, 220);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 5.0794ms @ 44.1kHz shall work (see test above).
    aos = aosw.Create(44100, 224);
    EXPECT_TRUE(aos->Open());
    aos->Close();

    // 2.9025ms is smaller than the minimum supported size (=3ms).
    aos = aosw.Create(44100, 132);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 3.01587ms is larger than the minimum size but is not aligned.
    // This test will propose an aligned buffer size of 3.6281ms.
    aos = aosw.Create(44100, 133);
    EXPECT_FALSE(aos->Open());
    aos->Close();

    // 3.6281ms @ 44.1kHz <=> smallest possible buffer size we can use.
    aos = aosw.Create(44100, 160);
    EXPECT_TRUE(aos->Open());
    aos->Close();
}

// Verify that we can open and start the output stream in exclusive mode at
// the lowest possible delay at 48kHz.
// It's disabled by default because a flag is required to enable exclusive mode.
TEST_F(WASAPIAudioOutputStreamTest,
    DISABLED_ExclusiveModeMinBufferSizeAt48kHz)
{
    ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndOutputDevices(audio_manager_.get()) && ExclusiveModeIsEnabled());

    MockAudioSourceCallback source;
    // Create exclusive-mode WASAPI output stream which plays out in stereo
    // using the minimum buffer size at 48kHz sample rate.
    AudioOutputStreamWrapper aosw(audio_manager_.get());
    AudioOutputStream* aos = aosw.Create(48000, 160);
    EXPECT_TRUE(aos->Open());

    // Derive the expected size in bytes of each packet.
    base::TimeDelta packet_duration = base::TimeDelta::FromSecondsD(
        static_cast<double>(aosw.samples_per_packet()) / aosw.sample_rate());

    // Wait for the first callback and verify its parameters.
    EXPECT_CALL(source,
        OnMoreData(HasValidDelay(packet_duration), _, 0, NotNull()))
        .WillOnce(DoAll(QuitLoop(message_loop_.task_runner()),
            Return(aosw.samples_per_packet())))
        .WillRepeatedly(Return(aosw.samples_per_packet()));

    aos->Start(&source);
    message_loop_.task_runner()->PostDelayedTask(
        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
        TestTimeouts::action_timeout());
    base::RunLoop().Run();
    aos->Stop();
    aos->Close();
}

// Verify that we can open and start the output stream in exclusive mode at
// the lowest possible delay at 44.1kHz.
// It's disabled by default because a flag is required to enable exclusive mode.
TEST_F(WASAPIAudioOutputStreamTest,
    DISABLED_ExclusiveModeMinBufferSizeAt44kHz)
{
    ABORT_AUDIO_TEST_IF_NOT(ExclusiveModeIsEnabled());

    MockAudioSourceCallback source;
    // Create exclusive-mode WASAPI output stream which plays out in stereo
    // using the minimum buffer size at 44.1kHz sample rate.
    AudioOutputStreamWrapper aosw(audio_manager_.get());
    AudioOutputStream* aos = aosw.Create(44100, 160);
    EXPECT_TRUE(aos->Open());

    // Derive the expected size in bytes of each packet.
    base::TimeDelta packet_duration = base::TimeDelta::FromSecondsD(
        static_cast<double>(aosw.samples_per_packet()) / aosw.sample_rate());

    // Wait for the first callback and verify its parameters.
    EXPECT_CALL(source,
        OnMoreData(HasValidDelay(packet_duration), _, 0, NotNull()))
        .WillOnce(DoAll(QuitLoop(message_loop_.task_runner()),
            Return(aosw.samples_per_packet())))
        .WillRepeatedly(Return(aosw.samples_per_packet()));

    aos->Start(&source);
    message_loop_.task_runner()->PostDelayedTask(
        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
        TestTimeouts::action_timeout());
    base::RunLoop().Run();
    aos->Stop();
    aos->Close();
}

} // namespace media
